#include <iostream>
#include <stdio.h>
#include <string.h>
#include <errno.h>

#include "arg_parser.h"



#include "directory.h"
#include "kfa_mux.h"


#include "rgb_to_nv12.h"
#include "kfa_writer.h"
#include "data_source.h"


using namespace std;

#if 0
#define LOG_TRACE() cout <<"[" << __FUNCTION__ << "][" << __LINE__<< "]===>:" << endl
#else
#define LOG_TRACE()
#endif


#include <sstream>

struct KfaMakerParam
{
    bool is_simulation; /** false: really write data to putput kfa file, true: not write data, just log out the info(e.g. file size, and so on) */
    kfa_raw_type raw_type;
    kfa_codec_format codec_format;
    uint32_t file_count;
    std::string input_path;
    std::string output_file;
};

static bool write_raw_to_file(const std::string& file_name, uint8_t* pic1_data, uint32_t pic_total_size)
{

    FILE* fp = fopen(file_name.c_str(), "wb");
    if (!fp)
    {
        cout << "open file: (" <<file_name <<") failed(err: " << strerror(errno) << endl;
        return false;
    }
    size_t ret_size = fwrite(pic1_data, 1, pic_total_size, fp);

    if (ret_size != pic_total_size)
    {
        cout << "write file req size: " << pic_total_size << "real size: " << ret_size << endl;
        fflush(fp);
        fclose(fp);
        return false;
    }

    fflush(fp);
    fclose(fp);  
    return true;    
}



static const char* get_subffix_by_raw_type(kfa_raw_type raw_type)
{
    switch (raw_type)
    {
    case kfa_raw_type::RGBA32:return ".rgba";    break;
    case kfa_raw_type::RGB24: return ".rgb";     break;
    case kfa_raw_type::NV12 : return ".nv12";    break;
    case kfa_raw_type::YUV420: return ".yuv420"; break;
    
    default:
        break;
    }
    return "";
}

#include <vector>

//using EncodeArray = std::vector<Lz4Encoder>;


static int make_kfa(const KfaMakerParam& param)
{
    cout << "input_path: " << param.input_path << ", out_file: " << param.output_file << endl;
    Directory::DirectoryPtr dir = Directory::OpenDir(param.input_path);

    if (!dir)
    {
        cout << "open dir(" << param.input_path << ") failed" << endl;
        return -1;
    }

    LOG_TRACE();
    KfaWrtier writer;

    LOG_TRACE();

    uint32_t file_idx = 0;

    DataSource data_source;
    LOG_TRACE();

    data_source.setOutputType(param.raw_type);
    LOG_TRACE();
    
  
    bool ret = dir->foreach([&writer, &data_source, &file_idx, &param](const std::string& path, bool isDir, uint32_t file_size) -> bool
    {
        //cout << "do encode and mux for file: " << path << ", is_dir: " << isDir << endl;
        if (isDir)
        {
            return true;
        }

        LOG_TRACE();
        uint16_t img_width, img_height;
        uint32_t pic_total_size = 0;
        unsigned char* pic1_data = data_source.getData(path, img_width, img_height, pic_total_size);
        LOG_TRACE();

        if (!pic1_data)
        {
            return true; /** todo refineme ?? */
        }
#if 1
        if (!writer.is_initialized())
        {
            KfaWriterInitializeParam init_param;
            init_param.file_name = param.output_file;
            init_param.header.width = img_width;
            init_param.header.height = img_height;
            init_param.header.raw_type = param.raw_type;
            init_param.header.codec_format = param.codec_format;
            if (writer.initialize(init_param, param.is_simulation) == false)
            {
                cout << "initialize writer failed\n" << endl;
                return false;
            }            
        }

        LOG_TRACE();
#if 1        
        bool ret = writer.write(pic1_data, pic_total_size, param.is_simulation, file_size); 
#else
        uint32_t frame_size = img_width * img_height;
        bool ret = writer.write(pic1_data, frame_size, param.is_simulation, file_size);
        ret = writer.write(pic1_data + frame_size, pic_total_size - frame_size, param.is_simulation, file_size);
#endif
#else
        std::stringstream ss;
        ss << "pic" << file_idx++ << get_subffix_by_raw_type(param.raw_type);
        write_raw_to_file(ss.str(), pic1_data, pic_total_size);   
        bool ret = file_idx < param.file_count;
#endif
        LOG_TRACE();
        delete[] pic1_data;
        LOG_TRACE();

        return ret;
    });

    cout << "output bytes: " << writer.total_out_size() << endl;
    LOG_TRACE();    

    LOG_TRACE();
    dir.reset();
    LOG_TRACE();

    return ret ? 0 : -1;
}

int main(int argc, char* argv[])
{
    KfaMakerParam maker_param;

    icpp::core::ArgParser a;

    a.addWithParamNoneHandler("help",'h', "show this usage info", [&a]() -> icpp::core::ArgumentParseError
    {
        a.showHelp();
        return icpp::core::ArgumentParseError::kDoCommand;
    });

    a.addWithParamRequired<std::string>("input", 'i', "the source file path(a dir)", true, maker_param.input_path);
    a.addWithParamRequired<std::string>("output", 'o', "output file like *.kfa", true, maker_param.output_file);
    
    

    a.addWithParamNone("simulation", 's', "if set,then This is a simulation of kfa file generation ", maker_param.is_simulation);

    a.addWithParamRequired<int32_t>("type", 't', "the raw type in kfa file(1: RGB32,2:GRB24, 3:yuv420, 4: NV12), now jut use default raw type from source png file or NV12", false, (int32_t&)maker_param.raw_type, (int32_t)kfa_raw_type::NV12);
    a.addWithParamRequired<int32_t>("encoder", 'e', "encoder codec format(1: LZ4, 2: ZSTD, 3: Tiny)", false, (int32_t&)maker_param.codec_format, (int32_t)kfa_codec_format::Tiny);
    //a.addWithParamRequired<int32_t>("capacity", 'c', "how many frame in one block", false, maker_param.frame_count, 2);
    //a.addWithParamRequired<int32_t>("display", 'd', "the display id to show the animation", false, init_param.display_id, 1);
    //a.addWithParamRequired<int32_t>("zorder", 'z', "the window zorder of the animation", false, init_param.zorder, 1000);

    icpp::core::ArgumentParseError ret = a.onParse(argc, argv);

    if (icpp::core::ArgumentParseError::kSuccess == ret)
    {
        make_kfa(maker_param);       
    }

    return 0;

}

